package com.smart.redis.client;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.smart.redis.PropertiesUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
/**
 *  
* @ClassName: RedisClient 
* @Description: TODO redis客户端实现及key操作
* @Company:e-smart.top
* @author Thinkpad 
* @version 1.0 2018年8月29日 下午12:39:04
 */
public class RedisClient {
 
	private static JedisPool pool;
	//禁止创建
	private RedisClient(){};
 
	/**
	 * 建立连接池 真实环境，一般把配置参数缺抽取出来。
	 * 
	 */
	private static void createJedisPool() {
 
		// 建立连接池配置参数
		JedisPoolConfig config = new JedisPoolConfig();
 
		// 设置最大连接数
		config.setMaxTotal(Integer.parseInt(PropertiesUtil.get("redis.pool.maxTotal")));
 
		// 设置最大阻塞时间，记住是毫秒数milliseconds
		config.setMaxWaitMillis(Long.parseLong(PropertiesUtil.get("redis.pool.maxWaitMillis")));
 
		// 设置最大空间连接
		config.setMaxIdle(Integer.parseInt(PropertiesUtil.get("redis.pool.maxIdle")));
		
		// 设置最小空间连接
		config.setMinIdle(Integer.parseInt(PropertiesUtil.get("redis.pool.minIdle")));
		
		// 创建连接池
		pool = new JedisPool(config, 
				PropertiesUtil.get("redis.host"), 
				Integer.parseInt(PropertiesUtil.get("redis.port")), 
				Integer.parseInt(PropertiesUtil.get("redis.timeout")), 
				PropertiesUtil.get("redis.password"), 
				Integer.parseInt(PropertiesUtil.get("redis.database")));
 
	}
 
	/**
	 * 在多线程环境同步初始化
	 */
	private static synchronized void poolInit() {
		if (pool == null)
			createJedisPool();
	}
 
	/**
	 * 获取一个jedis 对象
	 * 
	 * @return
	 */
	public static Jedis getJedis() {
 
		if (pool == null)
			poolInit();
		return pool.getResource();
	}
 
	/**
	 * 归还一个连接
	 * 
	 * @param jedis
	 */
	public static void returnToPool(Jedis jedis) {
        if(jedis != null) {
            jedis.close();
        }
    }
 
    /****************************key操作*************************************/
    /**
		DEL   
		DUMP
		EXISTS
		EXPIRE
		EXPIREAT
		KEYS
		MIGRATE
		MOVE
		OBJECT
		PERSIST
		PEXPIRE
		PEXPIREAT
		PTTL
		RANDOMKEY
		RENAME
		RENAMENX
		RESTORE
		SORT
		TTL
		TYPE
		SCAN
     **/
    
    /**
     * 删除一个 key
     * @param key
     */
    public static void delKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		jedis.del(key);
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}finally {
			returnToPool(jedis);
		}
    }
    /**
     * 批量删除key
     * @param key
     */
    public static void delKey(String... key){
    	Jedis jedis=getJedis();
    	try {
    		jedis.del(key);
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}finally {
			returnToPool(jedis);
		}
    }
 
    /**
     * 序列化key
     * @param key
     * @return
     */
    public static byte[] dumpKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		byte[] dump = jedis.dump(key);
    		return dump;
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}finally {
			returnToPool(jedis);
		}
    	return null;
    }
    /**
     * 查看key是否过期
     * @param key
     * @return
     */
    public static Boolean existsKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		Boolean exists = jedis.exists(key);
    		return exists;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 设置key的过期时间-多少秒后过期
     * @param key
     * @param seconds 单位 秒-s
     * @return
     */
    public static Boolean expireKey(String key,int seconds){
    	Jedis jedis=getJedis();
    	try {
    		Long expire = jedis.expire(key, seconds);
    		return expire==1||expire==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 设置key的过期时间-某个时间点过期
     * @param key
     * @param unixTime UNIX 时间戳-毫秒(unix timestamp)
     * @return
     */
    public static Boolean expireAtKey(String key,long unixTime){
    	Jedis jedis=getJedis();
    	try {
    		Long expireAt = jedis.expireAt(key, unixTime);
    		return expireAt==1||expireAt==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 查询符合要求的所有key-此方法很消耗性能，谨慎使用
     * 当redis中key数量越大，keys 命令执行越慢，而且最重要的会阻塞服务器
     * 
	 * KEYS * 匹配数据库中所有 key 。
	 * KEYS h?llo 匹配 hello ， hallo 和 hxllo 等。
	 * KEYS h*llo 匹配 hllo 和 heeeeello 等。
	 * KEYS h[ae]llo 匹配 hello 和 hallo ，但不匹配 hillo 。
	 * 特殊符号用 \ 隔开
     * @param pattern 
     * @return
     */
    @Deprecated
    public static Set<String> keysPat(String pattern){
    	Jedis jedis=getJedis();
    	try {
    		Set<String> keys = jedis.keys(pattern);
    		return keys;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 把当前库的key移动到指定库中
     * @param key
     * @param dbIndex redis库编号-一共有16个库 从0到15
     * @return 
     */
    public static Boolean moveKey(String key,int dbIndex){
    	Jedis jedis=getJedis();
    	try {
    		Long move = jedis.move(key, dbIndex);
    		return move==1||move==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 移除key的过期时间，使其持久化
     * @param key
     * @return
     */
    public static Boolean persistKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		Long persist = jedis.persist(key);
    		return persist==1||persist==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 设置key的过期时间-多少毫秒后过期
     * @param key
     * @param milliseconds 毫秒
     * @return
     */
    public static Boolean pexpireKey(String key,long milliseconds){
    	Jedis jedis=getJedis();
    	try {
    		Long persist = jedis.pexpire(key, milliseconds);
    		return persist==1||persist==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 设置key的过期时间-某个时间点过期
     * @param key
     * @param millisecondsTimestamp UNIX 时间戳-毫秒(unix timestamp)
     * @return
     */
    public static Boolean pexpireAtKey(String key,long millisecondsTimestamp){
    	Jedis jedis=getJedis();
    	try {
    		Long expireAt = jedis.pexpireAt(key, millisecondsTimestamp);
    		return expireAt==1||expireAt==1l;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return false;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 以毫秒为单位返回 key 的剩余生存时间
     * @param key
     * @return 毫秒
     */
    public static Long pttlKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		Long pttl = jedis.pttl(key);
    		return pttl;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 将 key 改名为 newkey 
     * 当 key 和 newkey 相同，或者 key 不存在时，返回一个错误。
     * 当 newkey 已经存在时， rename 命令将覆盖旧值。
     * @param oldKey 旧key
     * @param newKey 新key
     * @return
     */
    public static Boolean renameKey(String oldKey,String newKey){
    	Jedis jedis=getJedis();
    	try {
    		jedis.rename(oldKey, newKey);
    		return true;
		} catch (Exception e) {
			// TODO: renameKey 如果名称有重复会报错
			System.err.println("rename : "+e.getMessage());
			return false;
		} finally {
			returnToPool(jedis);
		}
    }
    /**
     * 将 key 改名为 newkey
     * 当且仅当 newkey 不存在时，将 key 改名为 newkey 
     * 当 key 不存在时，返回一个错误。
     * @param oldKey 旧key
     * @param newKey 新key
     * @return
     */
    public static Boolean renameNxKey(String oldKey,String newKey){
    	Jedis jedis=getJedis();
    	try {
    		Long renamenx = jedis.renamenx(oldKey, newKey);
    		return renamenx==1l||renamenx==1;
    	} catch (Exception e) {
    		// TODO: renameNxKey 如果名称有重复会报错
    		System.err.println("renamenx : "+e.getMessage());
    		return false;
    	} finally {
    		returnToPool(jedis);
    	}
    }
    /**
     * 以秒为单位，返回给定 key 的剩余生存时间(TTL, time to live)。
     * 当 key 不存在时，返回 -2 。
     * 当 key 存在但没有设置剩余生存时间时，返回 -1 。
     * 否则，以秒为单位，返回 key 的剩余生存时间。
     * @param key
     * @return 秒/s
     */
    public static Long ttlKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		Long ttl = jedis.ttl(key);
    		return ttl;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 获取key对应值的数据类型
     * none (key不存在)
     * string (字符串)
     * list (列表)
     * set (集合)
     * zset (有序集)
     * hash (哈希表)
     * @param key
     * @return
     */
    public static String typeKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		String type = jedis.type(key);
    		return type;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}finally {
			returnToPool(jedis);
		}
    	
    }
    /**
     * 
    * 可用此命令带起keys，此法查询key不会阻塞服务器
    * 
    * @Title: scanKey 
    * @Description: TODO 查询匹配key（**key**）的所有键集合
    * @param @param key
    * @param @return    设定文件 
    * @return List<String>    返回类型 
    * @throws
     */
    public static List<String> scanKey(String key){
    	Jedis jedis=getJedis();
    	try {
    		String cursor = "0";
    		List<String> keys = new ArrayList<String>();
    		ScanParams sp = new ScanParams();
    		sp.match("*"+key+"*");//匹配规则
    		sp.count(100);//每次查询数量
    		do{
                ScanResult<String> ret = jedis.scan(cursor, sp);
                List<String> result = ret.getResult();
                if(result!=null && result.size() > 0){
                    keys.addAll(result);
                }
                //再处理cursor
                cursor = ret.getStringCursor();
                System.out.println("cursor: "+cursor);
            }while(!cursor.equals("0"));
    		return keys;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return null;
		}finally {
			returnToPool(jedis);
		}
    }
    
    public static void main(String[] args) {
    	System.out.println(scanKey("key"));
	}
}